package com.xxrl.shop.config.log;

import com.xxrl.shop.domain.Log;
import com.xxrl.shop.service.LogService;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.Map;

/**
 * @author LinuY
 * @date 2020/7/24 16:08
 */
@Aspect
@Component
public class LogAspect {

    private static final Logger logger = LoggerFactory.getLogger(LogAspect.class);

    private Date BEGIN_TIME_THREAD_LOCAL;

    private final LogService logService;

    @Autowired(required = false)
    private HttpServletRequest request;

    private Log tbLog;

    public LogAspect(LogService logService) {
        this.logService = logService;
    }

    /**
     * Controller层切点,注解方式
     */
    @Pointcut("@annotation(com.xxrl.shop.config.log.ControllerLog)")
    public void controllerAspect() {
        // 切入点 空函数体
    }

    /**
     * Service层切点,注解方式
     */
    @Pointcut("@annotation(com.xxrl.shop.config.log.ServiceLog)")
    public void serviceAspect() {
        // 切入点 空函数体
    }

    @Before("controllerAspect()")
    public void controllerBefore(JoinPoint joinPoint) {
        before();
    }

    @Around("controllerAspect()")
    public Object controllerAround(ProceedingJoinPoint point) {
        Object result = null;
        try {
            result = point.proceed();
            String username = SecurityContextHolder.getContext().getAuthentication().getName();

            if (null != username) {
                setUpLog(username,1,point.getTarget().getClass().getName());
                logger.info("{}",tbLog);
            }
        } catch (Throwable e) {
            logger.error("AOP异常:{}",e.getMessage());
        }
        return result;
    }

    @After("controllerAspect()")
    public void controllerAfter(JoinPoint point) {
        after();
    }

    @Before("serviceAspect()")
    public void serviceBefore(JoinPoint joinPoint) {
        before();
    }

    @Around("serviceAspect()")
    public Object serviceAround(ProceedingJoinPoint point) {
        Object result = null;
        try {
            result = point.proceed();
            String username = SecurityContextHolder.getContext().getAuthentication().getName();

            if (null != username) {
                setUpLog(username,0, point.getTarget().getClass().getName());
                logger.info("{}",tbLog);
            }
        } catch (Throwable e) {
            logger.error("AOP后置通知异常:{0}",e);
        }
        return result;
    }

    /**
     * 异常通知 用于拦截service层记录异常日志
     */
    @After("serviceAspect()")
    public void serviceAfter(JoinPoint point) {
        after();
    }


    private void before() {
        BEGIN_TIME_THREAD_LOCAL = new Date();
        if (tbLog == null) {
            tbLog = new Log();
        }
    }
    private void setUpLog(String username,int logType,String className) {
        tbLog.setUserName(username);
        //日志类名
        tbLog.setClassName(className);
        //日志类型
        tbLog.setType(logType);
        //日志请求url
        tbLog.setUrl(request.getRequestURI());
        //请求方式
        tbLog.setRequestType(request.getMethod());
        //请求参数
        Map<String, String[]> logParams = request.getParameterMap();
        tbLog.setMapToParams(logParams);

        long beginTime = BEGIN_TIME_THREAD_LOCAL.getTime();
        tbLog.setCreateDate(beginTime);

    }
    private void after() {
        try {
            String username = SecurityContextHolder.getContext().getAuthentication().getName();

            if (null != username) {
                long beginTime = BEGIN_TIME_THREAD_LOCAL.getTime();
                long endTime = System.currentTimeMillis();
                //请求耗时
                long logElapsedTime = endTime - beginTime;
                tbLog.setLogElapsedTime((long) Math.toIntExact(logElapsedTime));
                tbLog.setCreateDate(beginTime);

                //调用线程保存至数据库
                SaveSystemLogThread saveSystemLogThread = new SaveSystemLogThread(tbLog, logService);
                saveSystemLogThread.run();
            }
        } catch (Exception e) {
            logger.error("AOP后置通知异常: {0}", e);
        }
    }

    /**
     * 保存日志
     */
    private static class SaveSystemLogThread implements Runnable {
        private final Log tbLog;
        private final LogService logService;
        public SaveSystemLogThread(Log tbLog, LogService logService) {
            this.tbLog = tbLog;
            this.logService = logService;
        }
        @Override
        public void run() {
            logService.save(tbLog);
        }
    }
}